#include "BernoulliNode.h"

#include <math.h>

#include <QDebug>

BernoulliNode::BernoulliNode(double sampleValue, bool isObserved) :
    Node(BERNOULLI, sampleValue, isObserved)
{

}

BernoulliNode::BernoulliNode(Node* parent, double sampleValue, bool isObserved) :
    Node(BERNOULLI, sampleValue, isObserved)
{
    this->addParent(parent);
}

BernoulliNode::BernoulliNode(const BernoulliNode& other) :
    Node(other),
    nodeParents(other.nodeParents)
{

}

BernoulliNode::~BernoulliNode()
{

}

double BernoulliNode::sample()
{
    if (!this->shouldSample()) {
        sampleHistory.append(lastSample);
        return lastSample;
    }

    double numerator = this->logLikelihood(1);
    lastSample = 1.0;
    foreach (Node* child, nodeChildren) {
        numerator += child->logLikelihood();
    }

    double denominator = this->logLikelihood(0);
    lastSample = 0.0;
    foreach (Node* child, nodeChildren) {
        denominator += child->logLikelihood();
    }

    denominator = exp(denominator) + exp(numerator);

    double probabilityOfBeingTrue = exp(numerator)/denominator;

    lastSample = Distribution::sharedInstance()->randomBernoulli(probabilityOfBeingTrue);

    sampleHistory.append(lastSample);

    return lastSample;

}

bool BernoulliNode::isInProbabilityTable(QString parentValues)
{
    return probabilityTable.contains(parentValues) || nodalProbabilityTable.contains(parentValues);
}

double BernoulliNode::getProbability(QString parentValues)
{
    if (nodalProbabilityTable.contains(parentValues)) {
        Node * node = nodalProbabilityTable.value(parentValues);
        return node->getLastSample();
    }

    return probabilityTable.value(parentValues);
}


double BernoulliNode::logLikelihood(double x)
{
    if (probabilityTable.isEmpty()) {
        return log(Distribution::sharedInstance()->likelihoodBernoulli(x, nodeParents.first()->getLastSample()));
    }

    QString parentValues;
    foreach (Node* parent, nodeParents) {
        parentValues.append(QString::number((int)parent->getLastSample()));

    }

    double probability = this->getProbability(parentValues); //probabilityTable.value(parentValues);

    if (nodeParents.isEmpty()) {
        parentValues = QString::number((int)x);        
        if (!this->isInProbabilityTable(parentValues)) {
            parentValues = QString::number(!(int)x);
        }
        probability = this->getProbability(parentValues); //probabilityTable.value(parentValues);
    }

    return log(Distribution::sharedInstance()->likelihoodBernoulli(x, probability));
}

void BernoulliNode::addTableEntry(QString parentValues, double probability)
{
    probabilityTable.insert(parentValues, probability);
}

void BernoulliNode::addTableEntry(QString parentValues, Node * probabilityNode)
{
    nodalProbabilityTable.insert(parentValues, probabilityNode);
}

void BernoulliNode::addParent(Node *parent)
{
    nodeParents.append(parent);
    parent->addChild(this);
}
